﻿using System;

namespace PropertyModels.ComponentModel
{
    /// <summary>
    /// Interface ICommand
    /// </summary>
    public interface IBaseCommand
    {
        /// <summary>
        /// Gets the name.
        /// </summary>
        /// <value>The name.</value>
        string Name { get; }

        /// <summary>
        /// Determines whether this instance can execute.
        /// </summary>
        /// <returns><c>true</c> if this instance can execute; otherwise, <c>false</c>.</returns>
        bool CanExecute();
        /// <summary>
        /// Executes this instance.
        /// </summary>
        /// <returns><c>true</c> if success, <c>false</c> otherwise.</returns>
        bool Execute();
    }

    /// <summary>
    /// Interface ICancelableCommand
    /// Implements the <see cref="PropertyModels.ComponentModel.IBaseCommand" />
    /// </summary>
    /// <seealso cref="PropertyModels.ComponentModel.IBaseCommand" />
    public interface ICancelableCommand : IBaseCommand
    {
        /// <summary>
        /// Determines whether this instance can cancel.
        /// </summary>
        /// <returns><c>true</c> if this instance can cancel; otherwise, <c>false</c>.</returns>
        bool CanCancel();

        /// <summary>
        /// Cancels this instance.
        /// </summary>
        /// <returns><c>true</c> if success, <c>false</c> otherwise.</returns>
        // ReSharper disable once UnusedMethodReturnValue.Global
        bool Cancel();
    }

    /// <summary>
    /// Class AbstractCommand.
    /// Implements the <see cref="PropertyModels.ComponentModel.IBaseCommand" />
    /// </summary>
    /// <seealso cref="PropertyModels.ComponentModel.IBaseCommand" />
    public abstract class AbstractBaseCommand : IBaseCommand
    {
        /// <summary>
        /// Gets the name.
        /// </summary>
        /// <value>The name.</value>
        // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
        public string Name { get; protected set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractBaseCommand"/> class.
        /// </summary>
        protected AbstractBaseCommand()
        {
            Name = GetType().Name;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractBaseCommand"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        protected AbstractBaseCommand(string name)
        {
            Name = name;
        }

        /// <summary>
        /// Determines whether this instance can execute.
        /// </summary>
        /// <returns><c>true</c> if this instance can execute; otherwise, <c>false</c>.</returns>
        public virtual bool CanExecute() => true;

        /// <summary>
        /// Executes this instance.
        /// </summary>
        /// <returns><c>true</c> if success, <c>false</c> otherwise.</returns>
        public abstract bool Execute();

        /// <summary>
        /// Returns a <see cref="string" /> that represents this instance.
        /// </summary>
        /// <returns>A <see cref="string" /> that represents this instance.</returns>
        public override string ToString() => Name;
    }

    /// <summary>
    /// Class GenericCommand.
    /// Implements the <see cref="PropertyModels.ComponentModel.AbstractBaseCommand" />
    /// </summary>
    /// <seealso cref="PropertyModels.ComponentModel.AbstractBaseCommand" />
    public class GenericCommand : AbstractBaseCommand
    {
        private readonly Func<bool>? _canExecuteFunc;
        private readonly Func<bool>? _executeFunc;

        /// <summary>
        /// Initializes a new instance of the <see cref="GenericCommand" /> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="executeFunc">The execute function.</param>
        /// <param name="canExecuteFunc">The can execute function.</param>
        public GenericCommand(string name, Func<bool>? executeFunc, Func<bool>? canExecuteFunc = null) :
            base(name)
        {
            _executeFunc = executeFunc;
            _canExecuteFunc = canExecuteFunc;
        }

        /// <summary>
        /// Determines whether this instance can execute.
        /// </summary>
        /// <returns><c>true</c> if this instance can execute; otherwise, <c>false</c>.</returns>
        public override bool CanExecute() => _canExecuteFunc?.Invoke() ?? base.CanExecute();

        /// <summary>
        /// Executes this instance.
        /// </summary>
        /// <returns><c>true</c> if success, <c>false</c> otherwise.</returns>
        public override bool Execute() => _executeFunc != null && _executeFunc();
    }

    /// <summary>
    /// Class AbstractCancelableCommand.
    /// Implements the <see cref="PropertyModels.ComponentModel.AbstractBaseCommand" />
    /// Implements the <see cref="PropertyModels.ComponentModel.ICancelableCommand" />
    /// </summary>
    /// <seealso cref="PropertyModels.ComponentModel.AbstractBaseCommand" />
    /// <seealso cref="PropertyModels.ComponentModel.ICancelableCommand" />
    public abstract class AbstractCancelableCommand : AbstractBaseCommand, ICancelableCommand
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractCancelableCommand"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        protected AbstractCancelableCommand(string name) :
            base(name)
        {
        }

        /// <summary>
        /// Determines whether this instance can cancel.
        /// </summary>
        /// <returns><c>true</c> if this instance can cancel; otherwise, <c>false</c>.</returns>
        public virtual bool CanCancel() => true;

        /// <summary>
        /// Cancels this instance.
        /// </summary>
        /// <returns><c>true</c> if success, <c>false</c> otherwise.</returns>
        public abstract bool Cancel();
    }

    /// <summary>
    /// Class GenericCancelableCommand.
    /// Implements the <see cref="PropertyModels.ComponentModel.AbstractCancelableCommand" />
    /// </summary>
    /// <seealso cref="PropertyModels.ComponentModel.AbstractCancelableCommand" />
    public class GenericCancelableCommand : AbstractCancelableCommand
    {
        private readonly Func<bool>? _canCancelFunc;
        private readonly Func<bool>? _canExecuteFunc;
        private readonly Func<bool>? _cancelFunc;
        private readonly Func<bool>? _executeFunc;

        /// <summary>
        /// Gets or sets the tag.
        /// </summary>
        /// <value>The tag.</value>
        // ReSharper disable once UnusedAutoPropertyAccessor.Global
        public object? Tag { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="GenericCancelableCommand"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="executeFunc">The execute function.</param>
        /// <param name="cancelFunc">The cancel function.</param>
        /// <param name="canExecuteFunc">The can execute function.</param>
        /// <param name="canCancelFunc">The can cancel function.</param>
        public GenericCancelableCommand(
            string name,
            Func<bool>? executeFunc,
            Func<bool>? cancelFunc,
            Func<bool>? canExecuteFunc = null,
            Func<bool>? canCancelFunc = null
            ) :
            base(name)
        {
            _executeFunc = executeFunc;
            _cancelFunc = cancelFunc;
            _canExecuteFunc = canExecuteFunc;
            _canCancelFunc = canCancelFunc;
        }

        /// <summary>
        /// Cancels this instance.
        /// </summary>
        /// <returns><c>true</c> if success, <c>false</c> otherwise.</returns>
        public override bool Cancel() => _cancelFunc != null && _cancelFunc();

        /// <summary>
        /// Executes this instance.
        /// </summary>
        /// <returns><c>true</c> if success, <c>false</c> otherwise.</returns>
        public override bool Execute() => _executeFunc != null && _executeFunc();

        /// <summary>
        /// Determines whether this instance can cancel.
        /// </summary>
        /// <returns><c>true</c> if this instance can cancel; otherwise, <c>false</c>.</returns>
        public override bool CanCancel() => _canCancelFunc?.Invoke() ?? base.CanCancel();

        /// <summary>
        /// Determines whether this instance can execute.
        /// </summary>
        /// <returns><c>true</c> if this instance can execute; otherwise, <c>false</c>.</returns>
        public override bool CanExecute() => _canExecuteFunc?.Invoke() ?? base.CanExecute();
    }
}
